package com.biz.primus.ms.oms.service.delivery;

import com.biz.primus.base.exception.BizSilentException;
import com.biz.primus.common.utils.TokenUtils;
import com.biz.primus.model.oms.enums.OrderDeliveryState;
import com.biz.primus.model.oms.enums.OrderPaymentState;
import com.biz.primus.model.oms.enums.returns.ReturnState;
import com.biz.primus.model.oms.vo.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.base.service.SequenceGenerator;
import com.biz.primus.ms.oms.dao.po.OmsOrder;
import com.biz.primus.ms.oms.dao.po.OmsOrderItem;
import com.biz.primus.ms.oms.dao.redis.GenerateTokenRedisDao;
import com.biz.primus.ms.oms.dao.repository.OmsOrderRepository;
import com.biz.primus.ms.oms.dao.repository.OmsOrderReturnItemRepository;
import com.biz.primus.ms.oms.dao.repository.OmsOrderReturnRepository;
import com.biz.primus.ms.oms.dao.repository.OmsPaymentRepository;
import com.biz.primus.ms.oms.dao.ro.GenerateTokenRo;
import com.biz.primus.model.oms.exception.OmsExceptions;
import com.biz.primus.ms.oms.service.collection.OrderDataConverter;
import com.biz.primus.ms.oms.service.core.OrderStateService;
import com.biz.primus.ms.oms.service.core.OrderStateService.OrderStateChangeRequest;
import com.biz.primus.ms.oms.stream.ReturnCollectionSource;
import com.biz.primus.ms.oms.trans.OrderItemConverter;
import com.biz.primus.ms.oms.vo.OmsOrderResource.OmsOrderResourceImpl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author yanweijin
 * @date 2017/11/8
 */
@Service
@Slf4j
public class DeliveryStateServiceImpl extends AbstractBaseService implements DeliveryStateService {

	@Autowired
	private GenerateTokenRedisDao generateTokenRedisDao;

	@Autowired
	private OmsOrderRepository omsOrderRepository;

	@Autowired
	private OmsOrderReturnRepository omsOrderReturnRepository;

	@Autowired
	private OmsOrderReturnItemRepository omsOrderReturnItemRepository;

	@Autowired
	private OmsPaymentRepository omsPaymentRepository;

	@Autowired
	private OrderDataConverter dataConverter;

	@Autowired
	private SequenceGenerator sequenceGenerator;

	@Autowired
	private OrderStateService orderStateService;

	@Autowired
	private OrderItemConverter itemConverter;

	@Autowired
	private ReturnCollectionSource returnCollectionSource;

	@Override
	public void notifyDeliveryApp(String orderCode) {

	}

	@Override
	public void notifyDeliveryAppCancel(String orderCode) {

	}

	@Override
	public String generateToken(OmsOrder order) {
		String generateToken = TokenUtils.generateNumberToken(10);
		GenerateTokenRo oldToken = generateTokenRedisDao.findByToken(generateToken);
		//如果生成了重复的token,则重新生成并返回
		if(oldToken!=null){
			return generateToken(order);
		}
		//把自提码放redis
		GenerateTokenRo tokenRo=new GenerateTokenRo();
		tokenRo.setGenerateToken(generateToken);
		tokenRo.setDepotCode(order.getDepotCode());
		tokenRo.setOrderCode(order.getOrderCode());
		generateTokenRedisDao.save(tokenRo);
		return generateToken;
	}

	@Override
	public String getOrderCodeByToken(String token, String depot) {
		GenerateTokenRo generateTokenRo = generateTokenRedisDao.findByToken(token);
		if(!Objects.equals(depot,generateTokenRo.getDepotCode())){
			throw new BizSilentException(OmsExceptions.DEPOT_ERROR);
		}
		return generateTokenRo.getOrderCode();
	}

	@Override
	public void orderPayAndReceipt(OrderPayAndReceiptVo vo) {
		OmsOrder o = orderStateService.findOrder(new OmsOrderResourceImpl(vo.getCode()));

		OrderPaymentState pstate = o.getPaymentState();

		/**
		 * 原支付状态:
		 *   cod:应该将支付状态推进到cash(isCash)或paid
		 *      如果是现金支付,
		 *   paid:不修改支付金额,并且需要退款
		 *   其他:不合法
		 */

		if(pstate == OrderPaymentState.cod){
			int actuallyPaid = verifyPayAmount(vo, o);
			o.setPaidAmount(actuallyPaid);
			if(vo.isNeedRecordCash()){
				o.setPaymentState(OrderPaymentState.cash);
			}else {
				//保存支付信息 todo 存疑?
//				List<OrderPaymentInfo> payments = dataConverter.paymentVo2Po(vo.getPayments(), o.getId());
//				omsPaymentRepository.save(payments);
				o.setPaymentState(OrderPaymentState.paid);
			}
		}else if(pstate == OrderPaymentState.paid){
		}else{
			throw new BizSilentException(OmsExceptions.STATE_ERROR);
		}


		//修改订单状态, 推进到finished,并且记录签收状态
		OrderStateChangeRequest req = OrderStateChangeRequest.builder().order(o).operator(vo.getOperator()).build();
		OrderDeliveryState deliveryState = vo.getReceiveType();
		orderStateService.finishOrder(req, deliveryState);


		//部分签收 生成退单
		if(deliveryState ==OrderDeliveryState.part_of_signed ||deliveryState == OrderDeliveryState.reject ){
			OmsOrderReturnVo returnVo = systemReturns(o, pstate, vo.getReceiving());
			Message<OmsOrderReturnVo> msg = MessageBuilder.withPayload(returnVo).build();
			//事务成功后,发出退单
			delayer.executeAfterTransactionCommit(()->returnCollectionSource.returnDataOutput().send(msg));
		}
	}


	/**
	 * 根据签收商品项,系统自动生成退货单
	 * 如果原支付状态为未完成,则不必退款,否则应该退款
	 * @param o
	 * @param originPaymentState
	 * @param receiptItemVos
	 * @return
	 */
	private OmsOrderReturnVo systemReturns(OmsOrder o,OrderPaymentState originPaymentState, List<ReceiptItemVo> receiptItemVos) {
		OmsOrderReturnVo rvo = new OmsOrderReturnVo();
		rvo.setReturnState(ReturnState.finished);
		rvo.setOriginOrderCode(o.getOrderCode());
		rvo.setChannel(o.getChannel());
		rvo.setOrderSource(o.getOrderSource());
		rvo.setReturnSource("system");
		rvo.setCode(sequenceGenerator.generateNextCode(SequenceGenerator.CodeType.returns));
		rvo.setDepot(o.getDepotCode());

		rvo.setNeedRefund(originPaymentState.isFinished());
		rvo.setUserId(o.getUserId());

		if(receiptItemVos==null){
			receiptItemVos = Collections.emptyList();
		}
		ImmutableMap<String, ReceiptItemVo> itemMap = Maps.uniqueIndex(receiptItemVos, ReceiptItemVo::getItemCode);
		List<OmsItemVo> items = o.getItems().stream().map((OmsOrderItem i) -> {
			ReceiptItemVo receiptItemVo = itemMap.get(i.getItemCode());
			Integer noReceipt = i.getQuantity();
			if (receiptItemVo != null) {
				if (Objects.equals(noReceipt, receiptItemVo.getQuantity())) {
					return null;
				}
				noReceipt -= receiptItemVo.getQuantity();
			}
			OmsItemVo oiv = itemConverter.po2vo(i);
			ItemNumberComputer computer = new ItemNumberComputer(noReceipt, i.getQuantity());
			computer.computeAndSet(i::getDiscountAmount, oiv::setDiscountAmount);
			computer.computeAndSet(i::getDiscountCoupon, oiv::setDiscountCoupon);
			computer.computeAndSet(i::getDiscountPop, oiv::setDiscountPop);
			computer.computeAndSet(i::getDiscountGrant, oiv::setDiscountGrant);
			computer.computeAndSet(i::getUsePoint, oiv::setUsePoint);
			oiv.setQuantity(noReceipt);
			return oiv;
		}).filter(Objects::nonNull).collect(Collectors.toList());
		//如果支付在之前已经完成,则需要退款
		if (originPaymentState.isFinished()) {
			rvo.setNeedRefund(true);
			int returnAmount = items.stream().mapToInt(i -> i.getQuantity() * i.getFinalPrice()).sum();
			rvo.setAmount(returnAmount);
		}
		rvo.setLiquidated(0);
		rvo.setItems(items);
		rvo.setSelfRemark("部分签收自动生成退单");
		return rvo;
	}

	private class ItemNumberComputer{
		double rate;

		ItemNumberComputer(Integer noReceiptQuantity, Integer totalQuantity) {
			this.rate = noReceiptQuantity.doubleValue()/totalQuantity.doubleValue();
		}

		void computeAndSet(Supplier<Integer> poAttrGetter,Consumer<Integer> voAttrSetter){
			voAttrSetter.accept((int)(poAttrGetter.get() - poAttrGetter.get() * rate));
		}
	}



	/**
	 * 验证支付金额
	 * @param vo
	 * @param o
	 */
	private int verifyPayAmount(OrderPayAndReceiptVo vo, OmsOrder o) {

		ImmutableMap<String, OmsOrderItem> itemMap = Maps.uniqueIndex(o.getItems(), OmsOrderItem::getItemCode);

		//货到付款的,要检查支付信息
		if(o.getPaymentState() == OrderPaymentState.cod){
			if (vo.getPayments()==null) {
				throw new BizSilentException(OmsExceptions.NEED_PAYMENTS_INFO);
			}
			int totalPaid = vo.getPayments().stream().mapToInt(OmsPaymentVo::getPaidAmount).sum();
			int totalReceipt = vo.getReceiving().stream().mapToInt(rvo -> {
				OmsOrderItem item = itemMap.get(rvo.getItemCode());
				return item.getQuantity() * item.getFinalPrice();
			}).sum();
			//实际支付少于应付款(签收商品 + 运费 + 调整值)
			if(totalPaid > totalReceipt + vo.getAdjustAmount() + o.getFreightAmount()){
				//todo 是否应该抛出异常
//				throw new BizSilentException()
			}
			return totalPaid;
		}
		return o.getPaidAmount();
	}


}
